<?php
abstract class AbstractAccounts
{
	// server and connection values
	protected $connection;
	protected $passwordtype;
	protected $passwordsalt;
	
	// last account id used
	protected static $lastid; // make method for it and static
	
	public function __construct(DataBase $connection, $passwordtype = 'plain', $passwordsalt = '')
	{
		$this->connection = $connection;
		$this->passwordtype = $passwordtype;
		$this->passwordsalt = $passwordsalt;
		
		if(!isset(self::$lastid))
		{
			// save it in class AND USE IT ONLY ONCE
			try
			{
				$stmt = $this->connection->query('SELECT COUNT(*) FROM accounts');
				$row = $stmt->fetch(DataBase::FETCH_NUM);
				$stmt->closeCursor();
				unset($stmt);
				
			
				self::$lastid = $row[0];
			}
			catch(Exception $e)
			{
				throw $e;
			}
		}
	}
	
	// // EXIST
	public function getAccountExistById($id)
	{
		try
		{
			$stmt = $this->connection->prepare('SELECT `id` FROM `accounts` WHERE `id` = :id');
			$stmt->bindParam(':id', $id, DataBase::PARAM_INT);
			$stmt->execute();
			
			$tmp = (array) $stmt->fetch(DataBase::FETCH_NUM);
			$count = $tmp[0];
			
			$stmt->closeCursor();
			unset($stmt);

			return (bool) $count;
		}
		catch(Exception $e)
		{
			throw $e;
		}
	}
	
	public function getAccountExistByName($name)
	{
		try
		{
			$name = strtolower($name);
			
			$stmt = $this->connection->prepare('SELECT LOWER(`name`) FROM `accounts` WHERE `name` = :name');
			$stmt->bindParam(':name', $name, DataBase::PARAM_STR);
			$stmt->execute();
			
			$rows = $stmt->fetch(PDO::FETCH_NUM);
			
			$stmt->closeCursor();
			unset($stmt);
			
			return (bool) $rows[0]; // count
		}
		catch(Exception $e)
		{
			throw $e;
		}
	}
	
	public function getAccountExistByEmail($email)
	{
		try
		{
			$stmt = $this->connection->prepare('SELECT `email` FROM `accounts` WHERE `email` = :email');
			$stmt->bindParam(':email', $email, DataBase::PARAM_STR);
			$stmt->execute();
			
			$rows = $stmt->fetch(PDO::FETCH_NUM);
			
			$stmt->closeCursor();
			unset($stmt);
			
			return (bool) $rows[0]; // count
		}
		catch(Exception $e)
		{
			throw $e;
		}
	}
	
	// // GET ACCOUNT FROM DATA BASE
	public function getAccountById($id)
	{
		if(!$this->getAccountExistById($id))
			throw new Exception('Unable too load account by id');
			
		try
		{
			$stmt = $this->connection->prepare('SELECT * FROM `accounts` WHERE `id` = :id');
			$stmt->bindParam(':id', $id, DataBase::PARAM_INT);
			$stmt->execute();
			
			$stmt = $stmt->fetch(DataBase::FETCH_ASSOC);
			
			return new Account($stmt);
		}
		catch(Exception $e)
		{
			throw $e;
		}
	}
	
	public function getAccountByName($name)
	{
		if(!$this->getAccountExistByName($name))
			throw new Exception('Unable too load account by name');
		
		try
		{
			$stmt = $this->connection->prepare('SELECT * FROM `accounts` WHERE `name` = :name');
			$stmt->bindParam(':name', $name, DataBase::PARAM_STR);
			$stmt->execute();
			
			$stmt = $stmt->fetch(DataBase::FETCH_ASSOC);
			
			return new Account($stmt);
		}
		catch(Exception $e)
		{
			throw $e;
		}
	}
	
	// // GET FIRST FREE ID // FOR NON mySQL SUPPORT
	public function getFreeId()
	{
		$next = (self::$lastid) +1;
		self::$lastid = $next;
		return $next;
	}
	
	// // GENERATE PASSWORD
	static function generatePassword()
	{
		$chars = '234567890abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
		$i = 0;
		$password = '';
		while ($i <= 10) {
			$password .= $chars{mt_rand(0,strlen($chars))};
			$i++;
		}
		return $password;
		// http://wiki.jumba.com.au/wiki/PHP_Generate_random_password
	}

	// // ENCRYPT
	public function encryptPassword($password)
	{
		try
		{
			switch ((string) $this->passwordtype)
			{
				case 'plain':
					$password = $password;
				break;
				
				case 'sha1':
					$password = sha1($this->passwordsalt.$password); // + sol
				break;
				
				case 'md5':
					$password = md5($this->passwordsalt.$password); // + sol
				break;
				
				default:
					throw new Exception('Unknown type of password encryption!');
			}
			return $password;
		}
		catch(Exception $e)
		{
			throw $e;
		}
		
	}
	

	// // SAVE
	public function save(Account $account, AccountValidator $validator)
	{
		$name = $account->getName();
		$email = $account->getEmail();
		
		if($name === null)
			throw new AccountException('Account don\'t have a name!'); // rzuc wyjatkiem?
	
		try
		{
			$validator->validateName($name);
		}
		catch(Exception $e)
		{
			throw $e;
		}
		
		if($email !== null)
		{
			try
			{
				$validator->validateEmail($email);
			}
			catch(Exception $e)
			{
				throw $e;
			}
			
			if($this->getAccountExistByEmail($email))
				throw new Exception('Account with this email alredy exists.');
		}
		
		if(!$account->passwordencrypted)
		{
			try
			{
				$pass = $account->getPassword();
				$validator->validatePassword($pass);
				$pass = $this->encryptPassword($pass);
				$account->setPassword($pass);
				$account->passwordencrypted = true;
			}
			catch(Exception $e)
			{
				throw $e;
			}
		}
	
		if($this->getAccountExistByName($name))
			throw new Exception('Account with this name alredy exists.');
		
	
		$querryConfigArray = array(
			'id' => array('param' => DataBase::PARAM_INT, 'name' => ':id'),
			'name' => array('param' => DataBase::PARAM_STR, 'name' => ':name'),
			'password' => array('param' => DataBase::PARAM_STR, 'name' => ':password'),
			'email' => array('param' => DataBase::PARAM_STR, 'name' => ':email'),
			'premend' => array('param' => DataBase::PARAM_INT, 'name' => ':premend'),
			'blocked' => array('param' => DataBase::PARAM_INT, 'name' => ':blocked'),
			'warnings' => array('param' => DataBase::PARAM_INT, 'name' => ':warnings')
		);
		
		$params = ''; $names = '';
		$executeArray = array();
			
		if($account->getAccountCondition() === Account::ACCOUNT_CREATED)
		{
			$id = $account->getId();
			if($id === null)
				$account->setId($this->getFreeId());
			elseif($this->getAccountExistById($id))
				throw new Exception('You can\'t create two accounts with same id.');

			$account->setAccountCondition(Account::ACCOUNT_LOADED);
			
			foreach($account->getBaseArrayCopy() as $key => $value)
			{
				$names = $names.$key.', ';
				$params = $params.$querryConfigArray[$key]['name'].', ';
				$executeArray[$querryConfigArray[$key]['name']] = array($value, $querryConfigArray[$key]['param']);
			}
			
			// deleting last coma
			$names = substr($names, 0, -2);
			$params = substr($params, 0, -2);
			
			try
			{
				$querry = $this->connection->prepare('INSERT INTO accounts ('.$names.') VALUES ('.$params.')');
				foreach($executeArray as $key => $arr)
				{
					$querry->bindParam($key, $arr[0], $arr[1]);
				}
				
				$ret = $querry->execute();
				
				$querry->closeCursor();
				unset($querry);

				return $ret;
			}
			catch(Exception $e)
			{
				throw $e;
			}
		}
		elseif($account->getAccountCondition() === Account::ACCOUNT_CHANGED)
		{
			$account->setAccountCondition(Account::ACCOUNT_LOADED);
			$keysandvalues = '';
			$executeArray = array();
			foreach($account->getBaseArrayCopy as $key => $value)
			{
				$keysandvalues = $keysandvalues.$key.'='.$querryConfigArray[$key]['name'].', ';
				$executeArray[$querryConfigArray[$key]['name']] = array($value, $querryConfigArray[$key]['param']);
			}
			
			// deleting last coma
			$keysandvalues = substr($keysandvalues, 0, -2);
			
			try
			{
				$querry = $this->connection->prepare('UPDATE accounts SET '.$keysandvalues.' WHERE id=:id');
				foreach($executeArray as $key => $arr)
				{
					$querry->bindParam($key, $arr[0], $arr[1]);
				}
				$ret = $querry->execute();
				
				$querry->closeCursor();
				unset($querry);

				return $ret;
			}
			catch(Exception $e)
			{
				throw $e;
			}
		}
		else
		{
			throw new Exception('You can\'t save unchanged account on same id!');
		}
		return true;
	}
	
}

class Accounts extends AbstractAccounts
{
	public function __construct(DataBase $connection, $passwordtype = 'plain', $passwordsalt = '')
	{
		parent::__construct($connection, $passwordtype, $passwordsalt);
	}
}

?>